/*
 * Copyright (C) 2012, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.benchmark;

import java.util.List;
import org.apache.log4j.Level;
import xowl.interpreter.OWL2Syntax;

/**
 *
 * @author Laurent WOUTERS
 */
public class CaseBenchmark {
    private static class Result {
        public long init;
        public long inc;
    }
    
    private List<java.lang.management.GarbageCollectorMXBean> gcs;
    private long gcTime;
    
    private xowl.interpreter.Repository repo;
    private xowl.lang.owl2.Ontology input;
    private xowl.lang.owl2.Ontology target;
    private xowl.lang.owl2.Ontology meta;
    private xowl.lang.owl2.Ontology cockpit;
    private xowl.lang.owl2.Ontology procedure;
    private xowl.lang.owl2.Ontology human;
    private int nextID;
    
    public void execute(String[] args) {
        if ("EOL".equals(args[1])) {
            writeEOL(args[0] + "_init.txt");
            writeEOL(args[0] + "_inc.txt");
        } else {
            Result result = execute(args[0], Integer.parseInt(args[1]));
            write(args[0] + "_init.txt", result.init);
            write(args[0] + "_inc.txt", result.inc);
        }
    }
    
    private void writeEOL(String file) {
        try {
            java.io.FileWriter writer = new java.io.FileWriter(file, true);
            writer.write("\n");
            writer.close();
        } catch (java.io.IOException ex) { }
    }
    
    private void write(String file, long value) {
        try {
            java.io.FileWriter writer = new java.io.FileWriter(file, true);
            writer.write("\t");
            writer.write(Long.toString(value));
            writer.close();
        } catch (java.io.IOException ex) { }
    }
    
    private Result execute(String name, int size) {
        gcs = java.lang.management.ManagementFactory.getGarbageCollectorMXBeans();
        
        xowl.utils.logging.Logger log = new xowl.utils.logging.Logger(CaseBenchmark.class.getName(), Level.OFF);
        xowl.interpreter.Interpreter.config(log, "../libraries/");
        repo = xowl.interpreter.Interpreter.createRepositoryNativeRDF(log);
        cockpit = repo.loadOntology("data/Cockpit.owl", OWL2Syntax.Functional);
        procedure = repo.loadOntology("data/Procedure.owl", OWL2Syntax.Functional);
        human = repo.loadOntology("data/Human.owl", OWL2Syntax.Functional);
        repo.loadOntology("data/Merge.owl", OWL2Syntax.Functional);
        input = repo.resolveOntology("http://www.xowl.org/benchmark/input");
        target = repo.resolveOntology("http://www.xowl.org/benchmark/target");
        meta = repo.resolveOntology("http://www.xowl.org/benchmark/meta");
        
        xowl.interpreter.Transformation transfo = getTransformation(name);
        repo.apply(transfo);
        xowl.interpreter.Changeset sample = generate(size);
        
        Result res = new Result();
        try {
            long begin = getTimeBegin();
            repo.apply(sample);
            long end = getTimeEnd();
            res.init = (end-begin);
            
            if (name.equals("Cockpit.Hierarchy"))
                sample = getIncrement_Hierarchy();
            else if (name.equals("Cockpit.SM"))
                sample = getIncrement_SM();
            else if (name.equals("Procedure"))
                sample = getIncrement_Procedure();
            else if (name.equals("Human.Process"))
                sample = getIncrement_Process();
            
            begin = getTimeBegin();
            repo.apply(sample);
            end = getTimeEnd();
            res.inc = (end-begin);
        } catch(xowl.interpreter.XOWLException ex) { }
        return res;
    }
    
    private long getTimeBegin() {
        gcTime = 0;
        for (java.lang.management.GarbageCollectorMXBean gc : gcs)
            gcTime += gc.getCollectionTime();
        return java.lang.System.currentTimeMillis();
    }
    
    private long getTimeEnd() {
        long now = java.lang.System.currentTimeMillis();
        for (java.lang.management.GarbageCollectorMXBean gc : gcs)
            now -= gc.getCollectionTime();
        return now + gcTime;
    }
    
    private xowl.interpreter.Transformation getTransformation(String name) {
        xowl.interpreter.RuleSet rules = repo.loadRules("data/" + name + ".transform.mv.owl", OWL2Syntax.Functional);
        return new xowl.interpreter.Transformation(input, target, meta, rules);
    }
    
    private xowl.interpreter.Changeset generate(int size) {
        xowl.interpreter.Changeset changeset = new xowl.interpreter.Changeset();
        for (int i=0; i!=size; i++)
            generate(changeset);
        return changeset;
    }
    
    private xowl.interpreter.Changeset getIncrement_Hierarchy() {
        xowl.interpreter.Changeset changeset = new xowl.interpreter.Changeset();
        for (int i=0; i!=200; i++) {
            xowl.lang.owl2.IRI zone = getNextEntity(input);
            changeset.add(axiom(resolve(cockpit, "Zone"), zone));
            changeset.add(axiom(zone, resolve(cockpit, "name"), "NewZone"));
        }
        return changeset;
    }
    
    private xowl.interpreter.Changeset getIncrement_SM() {
        xowl.interpreter.Changeset changeset = new xowl.interpreter.Changeset();
        for (int i=0; i!=200; i++) {
            xowl.lang.owl2.IRI state = getNextEntity(input);
            changeset.add(axiom(resolve(cockpit, "State"), state));
            changeset.add(axiom(state, resolve(cockpit, "name"), "NewState"));
        }
        return changeset;
    }
    
    private xowl.interpreter.Changeset getIncrement_Procedure() {
        xowl.interpreter.Changeset changeset = new xowl.interpreter.Changeset();
        for (int i=0; i!=200; i++) {
            xowl.lang.owl2.IRI proc = getNextEntity(input);
            changeset.add(axiom(resolve(procedure, "Procedure"), proc));
            changeset.add(axiom(proc, resolve(procedure, "description"), "NewProcedure"));
        }
        return changeset;
    }
    
    private xowl.interpreter.Changeset getIncrement_Process() {
        xowl.interpreter.Changeset changeset = new xowl.interpreter.Changeset();
        for (int i=0; i!=200; i++) {
            xowl.lang.owl2.IRI goal = getNextEntity(input);
            changeset.add(axiom(resolve(human, "Goal"), goal));
            changeset.add(axiom(goal, resolve(human, "name"), "NewGoal"));
        }
        return changeset;
    }
    
    private void generate(xowl.interpreter.Changeset changeset) {
        xowl.lang.owl2.IRI[] cd = generate_cockpit(changeset);
        xowl.lang.owl2.IRI[] hd = generate_human(changeset, cd[3], cd[4]);
        generate_procedure(changeset, hd[0], cd[1], cd[2]);
    }
    
    private xowl.lang.owl2.IRI[] generate_cockpit(xowl.interpreter.Changeset changeset) {
        xowl.lang.owl2.IRI name = resolve(cockpit, "name");
        xowl.lang.owl2.IRI components = resolve(cockpit, "components");
        
        xowl.lang.owl2.IRI category = getNextEntity(input);
        xowl.lang.owl2.IRI comp1 = getNextEntity(input);
        xowl.lang.owl2.IRI comp2 = getNextEntity(input);
        xowl.lang.owl2.IRI panel = getNextEntity(input);
        xowl.lang.owl2.IRI zone = getNextEntity(input);
        
        changeset.add(axiom(resolve(cockpit, "ComponentCategory"), category));
        changeset.add(axiomSub(category, resolve(cockpit, "Component")));
        changeset.add(axiom(resolve(cockpit, "GuardedButton"), comp1));
        changeset.add(axiom(resolve(cockpit, "GuardedButton"), comp1));
        changeset.add(axiom(resolve(cockpit, "Zone"), zone));
        changeset.add(axiom(resolve(cockpit, "Panel"), panel));
        changeset.add(axiom(zone, name, "MyZone"));
        changeset.add(axiom(panel, name, "MyPanel"));
        changeset.add(axiom(comp1, name, "Comp1"));
        changeset.add(axiom(comp2, name, "Comp2"));
        changeset.add(axiom(zone, components, panel));
        changeset.add(axiom(panel, components, comp1));
        changeset.add(axiom(panel, components, comp2));
        
        xowl.lang.owl2.IRI sm = getNextEntity(input);
        xowl.lang.owl2.IRI s1 = getNextEntity(input);
        xowl.lang.owl2.IRI s2 = getNextEntity(input);
        xowl.lang.owl2.IRI s3 = getNextEntity(input);
        xowl.lang.owl2.IRI t1 = getNextEntity(input);
        xowl.lang.owl2.IRI t2 = getNextEntity(input);
        xowl.lang.owl2.IRI t3 = getNextEntity(input);
        xowl.lang.owl2.IRI t4 = getNextEntity(input);
        xowl.lang.owl2.IRI i1 = getNextEntity(input);
        xowl.lang.owl2.IRI i2 = getNextEntity(input);
        
        changeset.add(axiom(resolve(cockpit, "StateMachine"), sm));
        changeset.add(axiom(resolve(cockpit, "State"), s1));
        changeset.add(axiom(resolve(cockpit, "State"), s2));
        changeset.add(axiom(resolve(cockpit, "State"), s3));
        changeset.add(axiom(resolve(cockpit, "Transition"), t1));
        changeset.add(axiom(resolve(cockpit, "Transition"), t2));
        changeset.add(axiom(resolve(cockpit, "Transition"), t3));
        changeset.add(axiom(resolve(cockpit, "Transition"), t4));
        changeset.add(axiom(resolve(cockpit, "Interaction"), i1));
        changeset.add(axiom(resolve(cockpit, "Interaction"), i2));
        changeset.add(axiom(category, resolve(cockpit, "sm"), sm));
        changeset.add(axiom(sm, resolve(cockpit, "states"), s1));
        changeset.add(axiom(sm, resolve(cockpit, "states"), s2));
        changeset.add(axiom(sm, resolve(cockpit, "init"), s1));
        changeset.add(axiom(t1, resolve(cockpit, "from"), s1));
        changeset.add(axiom(t1, resolve(cockpit, "to"), s2));
        changeset.add(axiom(t1, resolve(cockpit, "trigger"), i1));
        changeset.add(axiom(t2, resolve(cockpit, "from"), s2));
        changeset.add(axiom(t2, resolve(cockpit, "to"), s1));
        changeset.add(axiom(t2, resolve(cockpit, "trigger"), i1));
        changeset.add(axiom(t3, resolve(cockpit, "from"), s2));
        changeset.add(axiom(t3, resolve(cockpit, "to"), s3));
        changeset.add(axiom(t3, resolve(cockpit, "trigger"), i2));
        changeset.add(axiom(t4, resolve(cockpit, "from"), s3));
        changeset.add(axiom(t4, resolve(cockpit, "to"), s2));
        changeset.add(axiom(t4, resolve(cockpit, "trigger"), i2));
        
        changeset.add(axiom(s1, name, "State 1"));
        changeset.add(axiom(s2, name, "State 2"));
        changeset.add(axiom(s3, name, "State 3"));
        changeset.add(axiom(i1, name, "Interaction 1"));
        changeset.add(axiom(i2, name, "Interaction 2"));
        
        return new xowl.lang.owl2.IRI[] { category, comp1, comp2, i1, i2 };
    }
    
    private xowl.lang.owl2.IRI[] generate_human(xowl.interpreter.Changeset changeset, xowl.lang.owl2.IRI i1, xowl.lang.owl2.IRI i2) {
        xowl.lang.owl2.IRI goal = getNextEntity(input);
        xowl.lang.owl2.IRI plan = getNextEntity(input);
        xowl.lang.owl2.IRI dec = getNextEntity(input);
        xowl.lang.owl2.IRI br1 = getNextEntity(input);
        xowl.lang.owl2.IRI br2 = getNextEntity(input);
        xowl.lang.owl2.IRI ac11 = getNextEntity(input);
        xowl.lang.owl2.IRI ac12 = getNextEntity(input);
        xowl.lang.owl2.IRI ac21 = getNextEntity(input);
        xowl.lang.owl2.IRI ac22 = getNextEntity(input);
        
        changeset.add(axiom(resolve(human, "Capacity"), i1));
        changeset.add(axiom(resolve(human, "Capacity"), i2));
        
        changeset.add(axiom(resolve(human, "Goal"), goal));
        changeset.add(axiom(resolve(human, "GeneralPlan"), plan));
        changeset.add(axiom(resolve(human, "Decision"), dec));
        changeset.add(axiom(resolve(human, "Branch"), br1));
        changeset.add(axiom(resolve(human, "Branch"), br2));
        changeset.add(axiom(resolve(human, "Actuation"), ac11));
        changeset.add(axiom(resolve(human, "Actuation"), ac12));
        changeset.add(axiom(resolve(human, "Actuation"), ac21));
        changeset.add(axiom(resolve(human, "Actuation"), ac22));
        changeset.add(axiom(goal, resolve(human, "plan"), plan));
        changeset.add(axiom(plan, resolve(human, "root"), dec));
        changeset.add(axiom(dec, resolve(human, "branches"), br1));
        changeset.add(axiom(dec, resolve(human, "branches"), br2));
        changeset.add(axiom(br1, resolve(human, "steps"), ac11));
        changeset.add(axiom(br1, resolve(human, "steps"), ac12));
        changeset.add(axiom(br2, resolve(human, "steps"), ac21));
        changeset.add(axiom(br2, resolve(human, "steps"), ac22));
        changeset.add(axiom(ac11, resolve(human, "requires"), i1));
        changeset.add(axiom(ac21, resolve(human, "requires"), i1));
        changeset.add(axiom(ac12, resolve(human, "requires"), i2));
        changeset.add(axiom(ac22, resolve(human, "requires"), i2));
        
        changeset.add(axiom(ac11, resolve(human, "index"), 1));
        changeset.add(axiom(ac21, resolve(human, "index"), 2));
        changeset.add(axiom(ac12, resolve(human, "index"), 1));
        changeset.add(axiom(ac22, resolve(human, "index"), 2));
        changeset.add(axiom(goal, resolve(human, "name"), "MyGoal"));
        changeset.add(axiom(br1, resolve(human, "description"), "Branch 1"));
        changeset.add(axiom(br1, resolve(human, "description"), "Branch 2"));
        
        return new xowl.lang.owl2.IRI[] { goal };
    }
    
    private xowl.lang.owl2.IRI[] generate_procedure(xowl.interpreter.Changeset changeset, xowl.lang.owl2.IRI goal, xowl.lang.owl2.IRI c1, xowl.lang.owl2.IRI c2) {
        xowl.lang.owl2.IRI proc = getNextEntity(input);
        xowl.lang.owl2.IRI choice = getNextEntity(input);
        xowl.lang.owl2.IRI seq1 = getNextEntity(input);
        xowl.lang.owl2.IRI seq2 = getNextEntity(input);
        xowl.lang.owl2.IRI p11 = getNextEntity(input);
        xowl.lang.owl2.IRI p12 = getNextEntity(input);
        xowl.lang.owl2.IRI p13 = getNextEntity(input);
        xowl.lang.owl2.IRI p21 = getNextEntity(input);
        xowl.lang.owl2.IRI p22 = getNextEntity(input);
        xowl.lang.owl2.IRI p23 = getNextEntity(input);
        xowl.lang.owl2.IRI h11 = getNextEntity(input);
        xowl.lang.owl2.IRI h12 = getNextEntity(input);
        xowl.lang.owl2.IRI h21 = getNextEntity(input);
        xowl.lang.owl2.IRI h22 = getNextEntity(input);
        xowl.lang.owl2.IRI s1 = getNextEntity(input);
        xowl.lang.owl2.IRI s2 = getNextEntity(input);
        
        changeset.add(axiomSub(goal, resolve(procedure, "HumanAction")));
        
        changeset.add(axiom(resolve(procedure, "Procedure"), proc));
        changeset.add(axiom(resolve(procedure, "Choice"), choice));
        changeset.add(axiom(resolve(procedure, "Sequence"), seq1));
        changeset.add(axiom(resolve(procedure, "Sequence"), seq2));
        changeset.add(axiom(resolve(procedure, "SeqPart"), p11));
        changeset.add(axiom(resolve(procedure, "SeqPart"), p12));
        changeset.add(axiom(resolve(procedure, "SeqPart"), p13));
        changeset.add(axiom(resolve(procedure, "SeqPart"), p21));
        changeset.add(axiom(resolve(procedure, "SeqPart"), p22));
        changeset.add(axiom(resolve(procedure, "SeqPart"), p23));
        changeset.add(axiom(goal, h11));
        changeset.add(axiom(goal, h12));
        changeset.add(axiom(goal, h21));
        changeset.add(axiom(goal, h22));
        changeset.add(axiom(resolve(procedure, "HumanAction"), h11));
        changeset.add(axiom(resolve(procedure, "HumanAction"), h12));
        changeset.add(axiom(resolve(procedure, "HumanAction"), h21));
        changeset.add(axiom(resolve(procedure, "HumanAction"), h22));
        changeset.add(axiom(resolve(procedure, "SystemAction"), s1));
        changeset.add(axiom(resolve(procedure, "SystemAction"), s2));
        
        changeset.add(axiom(proc, resolve(procedure, "root"), choice));
        changeset.add(axiom(choice, resolve(procedure, "choices"), seq1));
        changeset.add(axiom(choice, resolve(procedure, "choices"), seq2));
        changeset.add(axiom(seq1, resolve(procedure, "parts"), p11));
        changeset.add(axiom(seq1, resolve(procedure, "parts"), p12));
        changeset.add(axiom(seq1, resolve(procedure, "parts"), p13));
        changeset.add(axiom(seq2, resolve(procedure, "parts"), p21));
        changeset.add(axiom(seq2, resolve(procedure, "parts"), p22));
        changeset.add(axiom(seq2, resolve(procedure, "parts"), p23));
        changeset.add(axiom(p11, resolve(procedure, "elem"), h11));
        changeset.add(axiom(p12, resolve(procedure, "elem"), s1));
        changeset.add(axiom(p13, resolve(procedure, "elem"), h12));
        changeset.add(axiom(p21, resolve(procedure, "elem"), h21));
        changeset.add(axiom(p22, resolve(procedure, "elem"), s2));
        changeset.add(axiom(p23, resolve(procedure, "elem"), h22));
        changeset.add(axiom(h11, resolve(procedure, "component"), c1));
        changeset.add(axiom(h12, resolve(procedure, "component"), c2));
        changeset.add(axiom(h21, resolve(procedure, "component"), c1));
        changeset.add(axiom(h22, resolve(procedure, "component"), c2));
        
        changeset.add(axiom(proc, resolve(procedure, "description"), "My Procedure"));
        changeset.add(axiom(proc, resolve(procedure, "description"), "My Procedure"));
        changeset.add(axiom(h11, resolve(procedure, "description"), "Activate C1"));
        changeset.add(axiom(h12, resolve(procedure, "description"), "Activate C2"));
        changeset.add(axiom(h21, resolve(procedure, "description"), "Activate C1"));
        changeset.add(axiom(h22, resolve(procedure, "description"), "Activate C2"));
        changeset.add(axiom(s1, resolve(procedure, "description"), "System 1"));
        changeset.add(axiom(s2, resolve(procedure, "description"), "System 2"));
        changeset.add(axiom(p11, resolve(procedure, "index"), 1));
        changeset.add(axiom(p12, resolve(procedure, "index"), 2));
        changeset.add(axiom(p13, resolve(procedure, "index"), 3));
        changeset.add(axiom(p21, resolve(procedure, "index"), 1));
        changeset.add(axiom(p22, resolve(procedure, "index"), 2));
        changeset.add(axiom(p23, resolve(procedure, "index"), 3));
        
        return new xowl.lang.owl2.IRI[] { proc };
    }
    
    private xowl.interpreter.Change axiom(xowl.lang.owl2.IRI classe, xowl.lang.owl2.IRI obj) {
        xowl.lang.owl2.ClassAssertion axiom = new xowl.lang.owl2.ClassAssertion();
        axiom.setClasse(classe);
        axiom.setIndividual(obj);
        return new xowl.interpreter.Change(input, axiom, true);
    }
    
    private xowl.interpreter.Change axiom(xowl.lang.owl2.IRI obj, xowl.lang.owl2.IRI prop, xowl.lang.owl2.IRI value) {
        xowl.lang.owl2.ObjectPropertyAssertion axiom = new xowl.lang.owl2.ObjectPropertyAssertion();
        axiom.setIndividual(obj);
        axiom.setObjectProperty(prop);
        axiom.setValueIndividual(value);
        return new xowl.interpreter.Change(input, axiom, true);
    }
    
    private xowl.interpreter.Change axiom(xowl.lang.owl2.IRI obj, xowl.lang.owl2.IRI prop, Object value) {
        xowl.lang.owl2.DataPropertyAssertion axiom = new xowl.lang.owl2.DataPropertyAssertion();
        axiom.setIndividual(obj);
        axiom.setDataProperty(prop);
        axiom.setValueLiteral(xowl.utils.Values.getExpression((xowl.lang.runtime.Literal)repo.getLiteral(value)));
        return new xowl.interpreter.Change(input, axiom, true);
    }
    
    private xowl.interpreter.Change axiomSub(xowl.lang.owl2.IRI sub, xowl.lang.owl2.IRI sup) {
        xowl.lang.owl2.SubClassOf axiom = new xowl.lang.owl2.SubClassOf();
        axiom.setClasse(sub);
        axiom.setSuperClass(sup);
        return new xowl.interpreter.Change(input, axiom, true);
    }
    
    private xowl.lang.owl2.IRI getNextEntity(xowl.lang.owl2.Ontology onto) {
        String iri = onto.getHasIRI().getHasValue() + "#gen" + Integer.toString(nextID);
        nextID++;
        return repo.getIRI(iri);
    }
    private xowl.lang.owl2.IRI resolve(xowl.lang.owl2.Ontology onto, String name) {
        String iri = onto.getHasIRI().getHasValue() + "#" + name;
        return repo.getIRI(iri);
    }
}
